package com.wn.access.service.impl;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wn.access.entity.BankReqDto;
import com.wn.access.entity.TradeLog;
import com.wn.access.mapper.TradeLogMapper;
import com.wn.access.service.ITradeLogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wn.common.model.dto.bank.BankBidDTO;
import com.wn.common.model.dto.bank.BankCardAddDto;
import com.wn.common.model.dto.bank.BankCardRequestDto;
import com.wn.common.model.dto.bank.BorrowRecordStatusBankDTO;
import com.wn.common.model.vo.bank.SignatureVo;
import com.wn.common.model.vo.borrow.BidBalanceDTO;
import com.wn.common.model.vo.borrow.BorrowRecordVo;
import com.wn.common.model.vo.borrow.LoanVo;
import com.wn.common.model.vo.pay.RepayVo;
import com.wn.common.result.BaseResult;
import com.wn.common.result.Result;
import com.wn.common.utils.EncodeUtil;
import com.wn.common.utils.ThreadPoolFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

/**
 * <p>
 * 托管系统交易记录表 服务实现类
 * </p>
 *
 * @author yellow docker
 * @since 2023-07-27
 *
 *     日志:
 *        log.info("xxx")
 *     1、记录
 *     2、等级 (控制方便)
 *           error
 *           info        上线
 *           warning
 *           debug       开发
 *           xxx
 *     3、日志的配置文件
 *           等级设置
 *           日志输出位置
 *           日志的输出格式
 *
 *   面试题: 设计模式 ？
 *   1、代理模式
 *         静态代理
 *         动态代理
 *             jdk 的 动态代理
 *             CGLIB 的动态代理
 *             spring的aop
 *         具体使用:
 *             mybatis mapper的原理
 *             openfeign 的接口实现
 *             spring的事务
 *
 *    单例
 *       Calendar 这个类
 *    装饰者模式
 *       java的io
 *     工厂
 *           mybatis 拿到的 xxxFactory
 *           spring 拿到的 BeanFactory
 *     xxx
 *     适配器模式
 *          mvc
 *
 */
@Slf4j
@Service
public class TradeLogServiceImpl extends ServiceImpl<TradeLogMapper, TradeLog> implements ITradeLogService {

    @Autowired
    private RestTemplate restTemplate;

    // 进行银行绑卡
    @Override
    public SignatureVo bankCardAddDto(BankCardAddDto bankCardAddDto) {

        //01  保存日志
        TradeLog tradeLog = new TradeLog();

        // 请求流水
        tradeLog.setReqCode(bankCardAddDto.getReqCode());

        //请求类型  使用字符串   标记
        tradeLog.setReqType("USER_CARD_CREATE");

        //标识 用户ID
        tradeLog.setObjectId(bankCardAddDto.getUserId());

        // 日志的创建实现
        tradeLog.setCreateDate(new Date());

        // 数据同步 (0 未同步) 1 同步
        tradeLog.setSynchronizeStatus(0);

        // 确认时间   (默认是当前的时间)
//        tradeLog.setConfirmDate(new Date());  //  确认时间 响应数据 (银行系统给我们返回的数据)
//        tradeLog.setResponseData("");

        // 通过线程池 把 数据存储到 mysql中
        // 线程池中 执行线程的方法  execute()   添加 Runnable
        /**
         *   这里有2个操作
         *      1、保存日志
         *      2、远程调用 银行系统
         *
         *    1、串行执行     A、B 排队执行
         *    2、并行执行     A、B 通过执行
         *
         *    1、并行操作可以考虑线程池
         *    2、异步操作可以考虑 线程池
         *    3、大数据处理的时候、可以开启多个线程执行 、统一返回结果
         */
        ThreadPoolFactory.threadPoolExecutor.execute(()->{
            save(tradeLog);
        });

        //02 对接银行业务
        log.info("模拟发送数据到银行");
        // 发送数据到银行  银行返回数据给你
        SignatureVo signatureVo = new SignatureVo();

        //服务的名称
        signatureVo.setServiceName("BANK_SERVICE_BIND");
        // 平台
        signatureVo.setPlatformNo("SDDFP");

        // 银行返回的数据
        String reqData = JSON.toJSONString(bankCardAddDto);
        signatureVo.setReqData(EncodeUtil.encodeURL(EncodeUtil.encodeBase64(reqData)));

        // 银行的URL
        signatureVo.setBankUrl("http://localhost:5600/bank/user");

        return signatureVo;
    }

    /**
     * 银行发送通知 日志同步
     * @param bankCardRequestDto
     */
    @Override
    public void updateTradeLog(BankCardRequestDto bankCardRequestDto) {

        //更新操作
        update(Wrappers.lambdaUpdate(TradeLog.class)
                .set(TradeLog::getSynchronizeStatus,1)      //数据同步
                .set(TradeLog::getConfirmDate, new Date())  //确认时间
                .eq(TradeLog::getReqCode,bankCardRequestDto.getReqCode())  // 通过流水查询
        );
    }

    /**
     * 借款记录同步到 银行系统
     *   托管服务 2个事情:
     *     1、日志记录
     *     2、发送数据到 银行系统
     *
     * @param borrowRecordVo
     */
    @Override
    public BaseResult syncBorrowRecord(BorrowRecordVo borrowRecordVo) {

        //01  记录日志
        TradeLog tradeLog = new TradeLog();

        tradeLog.setReqCode("BORROW_"+ UUID.randomUUID().toString().substring(0,8));
        tradeLog.setReqType("BORROW");
        //关联  主体的标识
        tradeLog.setObjectId(borrowRecordVo.getId().toString());
        tradeLog.setCreateDate(new Date());
        //数据是否同步
        tradeLog.setSynchronizeStatus(0);
        tradeLog.setConfirmDate(new Date());

        //返回数据  （银行系统返回数据）
        // 银行系统 返回数据后 更新  status、ResponseData
        tradeLog.setResponseData("");

//        CountDownLatch countDownLatch = new CountDownLatch(2);

        ThreadPoolFactory.threadPoolExecutor.execute(()->{
            save(tradeLog);
//            countDownLatch.countDown();
        });

        //02 发送数据给银行系统  看银行系统
        // url:  /service    serviceName= xxx 服务(   SYNC_BORROW_RECODE)
        // 请求数据:  reqData
        // String url = "http://localhost:5600/service?serviceName=SYNC_BORROW_RECODE&reqData=xxx";
        //发送数据   加密的问题
        log.info("发送银行数据...");
        //返回结果  不能是obj 是封装的 BaseResult
        //把  borrowRecordVo json 字符串
        String reqData = JSON.toJSONString(borrowRecordVo);
        //加密
        reqData  = EncodeUtil.encodeBase64(reqData);
        //转成 url编码
        reqData = EncodeUtil.encodeURL(reqData);

        String url = "http://localhost:5600/service?serviceName=SYNC_BORROW_RECORD&reqData=" + reqData;
        log.info("发送数据的url:{}",url);

        /**
         *   t1  线程     添加日志
         *   t2  线程     发送数据到银行系统
         *
         *   闭锁、t1、t2 执行完毕后
         *   更新日志
         *   拿到银行数据、更新日志
         */
        // 银行系统   使用的是 @RestController   把 对象都转成 json 字符串
        String respBody = restTemplate.getForObject(url, String.class);

        log.info("银行系统返回的数据:{}",respBody);

        BankReqDto bankReqDto = JSON.parseObject(respBody, BankReqDto.class);
        //拿到字符串解析
        BaseResult baseResult = JSON.parseObject(bankReqDto.getRespData(), BaseResult.class);

        log.info("银行系统返回的数据进行解析:{}",baseResult);
        String respMsg = baseResult.getRespMsg();

        if (respMsg.equals("成功")){
            //更新日志  更新  status、ResponseData
            update(
                    Wrappers.lambdaUpdate(TradeLog.class)
                            .set(TradeLog::getSynchronizeStatus,1)
                            .set(TradeLog::getResponseData,respBody)   // 银行响应的数据
                            .set(TradeLog::getConfirmDate,new Date())
                            .eq(TradeLog::getId,tradeLog.getId())
            );
        }
        return baseResult;
    }

    @Override
    public BidBalanceDTO getBalanceDetails(String userCode) {

        //1记录日志
        //2、查询银行的余额
        String url = "http://localhost:5600/bid/balance/" + userCode;
        log.info("发送数据的url:{}",url);
        BidBalanceDTO bidBalanceDTO = restTemplate.getForObject(url, BidBalanceDTO.class);
        log.info("银行返回的数据:{}",bidBalanceDTO);

        return bidBalanceDTO;
    }

    @Override
    public BaseResult saveAgencyBid(BankBidDTO bankBidDTO) {

        //01  记录日志

        TradeLog tradeLog = new TradeLog();

        tradeLog.setReqCode("BID"+ UUID.randomUUID().toString().substring(0,8));
        tradeLog.setReqType("BID");
        //关联  主体的标识
        tradeLog.setObjectId(bankBidDTO.getId());
        tradeLog.setCreateDate(new Date());
        //数据是否同步
        tradeLog.setSynchronizeStatus(0);
        tradeLog.setConfirmDate(new Date());

        //返回数据  （银行系统返回数据）
        // 银行系统 返回数据后 更新  status、ResponseData
        ThreadPoolFactory.threadPoolExecutor.execute(()->{
            save(tradeLog);
        });

        //02  发送数据到银行系统
        log.info("发送银行数据...");
        String reqData = JSON.toJSONString(bankBidDTO);
        //加密
        reqData  = EncodeUtil.encodeBase64(reqData);
        //转成 url编码
        reqData = EncodeUtil.encodeURL(reqData);


        String url = "http://localhost:5600/service?serviceName=AGENCY_BID&reqData=" + reqData;
        log.info("发送数据的url===>:{}",url);

        String respBody = restTemplate.getForObject(url, String.class);

        log.info("银行系统返回的数据:{}",respBody);

        BankReqDto bankReqDto = JSON.parseObject(respBody, BankReqDto.class);
        //拿到字符串解析
        BaseResult baseResult = JSON.parseObject(bankReqDto.getRespData(), BaseResult.class);

        log.info("银行系统返回的数据进行解析:{}",baseResult);
        String respMsg = baseResult.getRespMsg();

        if (respMsg.equals("成功")){
            //更新日志  更新  status、ResponseData
            update(
                    Wrappers.lambdaUpdate(TradeLog.class)
                            .set(TradeLog::getSynchronizeStatus,1)
                            .set(TradeLog::getResponseData,respBody)   // 银行响应的数据
                            .set(TradeLog::getConfirmDate,new Date())
                            .eq(TradeLog::getId,tradeLog.getId())
            );
        }

        return baseResult;
    }

    @Override
    public BaseResult confirmLoan(LoanVo loanVo) {

        //01  保存日志
        TradeLog tradeLog = new TradeLog();

        tradeLog.setReqCode("LOAN"+ UUID.randomUUID().toString().substring(0,8));
        tradeLog.setReqType("LOAN");
        //关联  主体的标识
        tradeLog.setObjectId(loanVo.getId().toString());
        tradeLog.setCreateDate(new Date());
        //数据是否同步
        tradeLog.setSynchronizeStatus(0);
        tradeLog.setConfirmDate(new Date());

        //返回数据  （银行系统返回数据）
        // 银行系统 返回数据后 更新  status、ResponseData
        ThreadPoolFactory.threadPoolExecutor.execute(()->{
            save(tradeLog);
        });

        //02  发送数据到银行系统
        log.info("发送银行数据...");

        //02 发送数据到 银行系统

        String reqData = JSON.toJSONString(loanVo);
        //加密
        reqData  = EncodeUtil.encodeBase64(reqData);
        //转成 url编码
        reqData = EncodeUtil.encodeURL(reqData);

        String url = "http://localhost:5600/service?serviceName=CONFIRM_LOAN&reqData=" + reqData;
        log.info("发送数据的url===>:{}",url);

        String respBody = restTemplate.getForObject(url, String.class);

        log.info("银行系统返回的数据:{}",respBody);

        BankReqDto bankReqDto = JSON.parseObject(respBody, BankReqDto.class);
        //拿到字符串解析
        BaseResult baseResult = JSON.parseObject(bankReqDto.getRespData(), BaseResult.class);

        log.info("银行系统返回的数据进行解析:{}",baseResult);
        String respMsg = baseResult.getRespMsg();

        if (respMsg.equals("成功")){
            //更新日志  更新  status、ResponseData
            update(
                    Wrappers.lambdaUpdate(TradeLog.class)
                            .set(TradeLog::getSynchronizeStatus,1)
                            .set(TradeLog::getResponseData,respBody)   // 银行响应的数据
                            .set(TradeLog::getConfirmDate,new Date())
                            .eq(TradeLog::getId,tradeLog.getId())
            );
        }

        return baseResult;
    }

    @Override
    public BaseResult confirmRepay(RepayVo repaymentRequest) {
        //01  保存日志
        TradeLog tradeLog = new TradeLog();

        tradeLog.setReqCode("Repayment"+ UUID.randomUUID().toString().substring(0,8));
        tradeLog.setReqType("Repayment");
        //关联  主体的标识
        tradeLog.setObjectId(repaymentRequest.getId().toString());
        tradeLog.setCreateDate(new Date());
        //数据是否同步
        tradeLog.setSynchronizeStatus(0);
        tradeLog.setConfirmDate(new Date());

        //返回数据  （银行系统返回数据）
        // 银行系统 返回数据后 更新  status、ResponseData
        ThreadPoolFactory.threadPoolExecutor.execute(()->{
            save(tradeLog);
        });

        //02  发送数据到银行系统
        log.info("发送银行数据...");

        //02 发送数据到 银行系统

        String reqData = JSON.toJSONString(repaymentRequest);
        //加密
        reqData  = EncodeUtil.encodeBase64(reqData);
        //转成 url编码
        reqData = EncodeUtil.encodeURL(reqData);

        String url = "http://localhost:5600/service?serviceName=CONFIRM_REPAYMENT&reqData=" + reqData;
        log.info("发送数据的url===>:{}",url);

        String respBody = restTemplate.getForObject(url, String.class);

        log.info("银行系统返回的数据:{}",respBody);

        BankReqDto bankReqDto = JSON.parseObject(respBody, BankReqDto.class);
        //拿到字符串解析
        BaseResult baseResult = JSON.parseObject(bankReqDto.getRespData(), BaseResult.class);

        log.info("银行系统返回的数据进行解析:{}",baseResult);
        String respMsg = baseResult.getRespMsg();

        if (respMsg.equals("成功")){
            //更新日志  更新  status、ResponseData
            update(
                    Wrappers.lambdaUpdate(TradeLog.class)
                            .set(TradeLog::getSynchronizeStatus,1)
                            .set(TradeLog::getResponseData,respBody)   // 银行响应的数据
                            .set(TradeLog::getConfirmDate,new Date())
                            .eq(TradeLog::getId,tradeLog.getId())
            );
        }

        return baseResult;
    }

    @Override
    public Result changeBorrowStatus(BorrowRecordStatusBankDTO borrowRecordStatusBankDTO) {

        //01 保存日志
        TradeLog tradeLog = new TradeLog();

        tradeLog.setReqCode("BORROW"+ UUID.randomUUID().toString().substring(0,8));
        tradeLog.setReqType("BORROW_STATUS");
        //关联  主体的标识
        tradeLog.setObjectId(borrowRecordStatusBankDTO.getBorrowRecordId().toString());
        tradeLog.setCreateDate(new Date());
        //数据是否同步
        tradeLog.setSynchronizeStatus(0);
        tradeLog.setConfirmDate(new Date());

        //返回数据  （银行系统返回数据）
        // 银行系统 返回数据后 更新  status、ResponseData
        ThreadPoolFactory.threadPoolExecutor.execute(()->{
            save(tradeLog);
        });

        //02  发送数据到银行系统
        log.info("发送银行数据...");

        String reqData = JSON.toJSONString(borrowRecordStatusBankDTO);
        //加密
        reqData  = EncodeUtil.encodeBase64(reqData);
        //转成 url编码
        reqData = EncodeUtil.encodeURL(reqData);

        String url = "http://localhost:5600/service?serviceName=UPDATE_BORROW_STATUS&reqData=" + reqData;
        log.info("发送数据的url===>:{}",url);

        String respBody = restTemplate.getForObject(url, String.class);

        log.info("银行系统返回的数据:{}",respBody);

        //拿到字符串解析
        Result result = JSON.parseObject(respBody, Result.class);

        log.info("银行系统返回的数据进行解析:{}",result);


        if (result.getCode() == 200){
            //更新日志  更新  status、ResponseData
            update(
                    Wrappers.lambdaUpdate(TradeLog.class)
                            .set(TradeLog::getSynchronizeStatus,1)
                            .set(TradeLog::getResponseData,respBody)   // 银行响应的数据
                            .set(TradeLog::getConfirmDate,new Date())
                            .eq(TradeLog::getId,tradeLog.getId())
            );
        }
        return result;
    }
}
